/* 
 * $Id: PDFStream.java,v 1.1.1.1 2001/10/29 19:51:08 ezb Exp $
 *
 * $Date: 2001/10/29 19:51:08 $
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public 
 * License as published by the Free Software Foundation; either 
 * version 2.1 of the License, or (at your option) any later version. 
 * 
 * This library is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
 * Lesser General Public License for more details. 
 * 
 * You should have received a copy of the GNU Lesser General Public 
 * License along with this library; if not, write to the Free Software 
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 */
package gnu.jpdf;

import java.io.*;
import java.util.zip.*;

/**
 * This class implements a PDF stream object. In PDF, streams contain data
 * like the graphic operators that render a page, or the pixels of an image.
 *
 * <p>In PDF, a stream can be compressed using several different methods, or
 * left uncompressed. Here we support both uncompressed, and FlateDecode as
 * it's supported by the java core.
 *
 * @author Peter T Mount http://www.retep.org.uk/pdf/
 * @author Eric Z. Beard, ericzbeard@hotmail.com
 * @author $Author: ezb $
 * @version $Revision: 1.1.1.1 $, $Date: 2001/10/29 19:51:08 $
 *
 */
public class PDFStream extends PDFObject implements Serializable
{
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	/*
	 * NOTE: The original class is the work of Peter T. Mount, who released it 
	 * in the uk.org.retep.pdf package.  It was modified by Eric Z. Beard as 
	 * follows: 
	 * The package name was changed to gnu.jpdf.  
	 * The formatting was changed a little bit.
	 * It is still licensed under the LGPL.
	 */

	/**
	 * This holds the stream's content.
	 */
	transient ByteArrayOutputStream buf;

	/**
	 * True if we will compress the stream in the pdf file
	 */
	boolean deflate;

	/**
	 * Constructs a plain stream.
	 * <p>By default, the stream will be compressed.
	 */
	public PDFStream() 
	{this(null);}

	/**
	 * Constructs a stream. The supplied type is stored in the stream's header
	 * and is used by other objects that extend the PDFStream class (like
	 * PDFImage).
	 * <p>By default, the stream will be compressed.
	 * @param type type for the stream
	 * @see PDFImage
	 */
	public PDFStream(String type) 
	{
		super(type);
		buf = new ByteArrayOutputStream();
		// default deflate mode
		deflate = false;
	}

	/**
	 * @param mode true will FlatDecode the stream
	 */
	public void setDeflate(boolean mode) 
	{deflate = mode;}

	/**
	 * Returs true if the stream will be compressed.
	 * @return true if compression is enabled
	 */
	public boolean getDeflate()
	{return deflate;}

	/**
	 * Returns the OutputStream that will append to this stream.
	 * @return The stream for this object
	 */
	public OutputStream getOutputStream() 
	{return (OutputStream)buf;}

	/**
	 * Creates a PrintWriter that will append to this stream.
	 * @return a PrintWriter to write to the stream
	 */
	public PrintWriter getWriter() 
	{return new PrintWriter(buf,true);}

	/**
	 * This is for extenders, and provides access to the stream.
	 * @return ByteArrayOutputStream containing the contents.
	 */
	public ByteArrayOutputStream getStream() 
	{return buf;}

	/**
	 * @param os OutputStream to send the object to
	 * @exception IOException on error
	 */
	public void write(OutputStream os) throws IOException 
	{
		writeStart(os);
		writeStream(os);
		// Unlike most PDF objects, we dont call writeEnd(os) because we
		// contain a stream
	}

	/**
	 * This inserts the Streams length, then the actual stream, finally
	 * the end of stream/object markers.
	 *
	 * <p>This is intended for anyone extending PDFStream, as objects
	 * containing streams do no use writeEnd(), and they must be able
	 * to write the actual stream.
	 *
	 * @param os OutputStream to send the object to
	 * @exception IOException on error
	 */
	public void writeStream(OutputStream os) throws IOException 
	{
		if(deflate) 
		{
			ByteArrayOutputStream b = new ByteArrayOutputStream();
			DeflaterOutputStream dos = new DeflaterOutputStream(b);
			//,new Deflater(Deflater.BEST_COMPRESSION,true));
			buf.writeTo(dos);
			dos.finish();
			dos.close();

			// FlatDecode is compatible with the java.util.zip.Deflater class
			os.write("/Filter /FlateDecode\n".getBytes());
			os.write("/Length ".getBytes());
			os.write(Integer.toString(b.size()+1).getBytes());
			os.write("\n>>\nstream\n".getBytes());
			b.writeTo(os);
			os.write("\n".getBytes());
		} 
		else 
		{
			// This is a non-deflated stream
			os.write("/Length ".getBytes());
			os.write(Integer.toString(buf.size()).getBytes());
			os.write("\n>>\nstream\n".getBytes());
			buf.writeTo(os);
		}
		os.write("endstream\nendobj\n".getBytes());
		// Unlike most PDF objects, we dont call writeEnd(os) because we
		// contain a stream
	}
	// Why is this here?  Did it have a specific purpose?

	/**
	 * This implements our own special Serialization for this object.
	 *
	 * <p>Here we write the length of the stream's contents, then a byte
	 * array of the contents. We have to do this, as ByteArrayOutputStream
	 * is not serializable (hence the transient tag).
	 *
	 */
	private void writeObject(java.io.ObjectOutputStream out) throws IOException
	{
		out.writeInt(buf.size());
		out.write(buf.toByteArray());
	}

	/**
	 * This implements our own special Serialization for this object
	 *
	 * <p>Here we read the length of the stream's contents, then a byte
	 * array of the contents. Then we recreate a new ByteArrayOutputStream.
	 * We have to do this, as ByteArrayOutputStream is not serializable
	 * (hence the transient tag).
	 *
	 */
	private void readObject(java.io.ObjectInputStream in) throws IOException
	{
		int l = in.readInt();
		byte b[] = new byte[l];
		in.read(b,0,l);
		buf=new ByteArrayOutputStream(l);
		buf.write(b);
	}
}